import { Component, OnInit, ViewChild, ElementRef, Input } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import * as $ from 'jquery';

@Component({
  selector: 'json-read-only',
  templateUrl: './jsonReadOnly.html',
  styleUrls: ['./jsonReadOnly.css']
})
export class JsonReadOnlyComponent implements OnInit {

  @Input('jsonConfig') _jsonConfig: JsonModelConfig;
  @ViewChild('codeTemp') _codeTemp: ElementRef;
  _jsonMeta: Meta;
  _jsonStr: string = "";

  _color = {
    number: 'red',
    string: 'green',
    null: '#bfbfbf',
    boolean: 'orange',
  }

  constructor() { }

  ngOnInit() {
    switch (this._jsonConfig.mode) {
      case 'fold':
        this._jsonMeta = this._formatJson(this._jsonConfig.data);
        break;
      case 'view':
        this._jsonStr = this._indentJson(this._jsonConfig.data);
        break;
    }
  }

  ngAfterViewInit() { }

  /**
   * 获取数据类型
   * @param data 
   */
  _getDateType(data: any): string {
    if (typeof data == 'number') {
      return 'number';
    } else if (typeof data == 'string') {
      return 'string';
    } else if (typeof data == 'undefined') {
      return 'undefined';
    } else if (typeof data == 'boolean') {
      return 'boolean';
    } else if (data === null) {
      return 'null';
    } else if (typeof data == 'object') {
      if (data instanceof Array) {
        return 'array';
      } else if (data instanceof Function) {
        return 'function';
      } else if (data instanceof Object) {
        return 'object';
      } else {
        console.log(data, '暂未处理此类型');
        return undefined;
      }
    } else {
      console.log(data, '暂未处理此类型');
      return undefined;
    }
  }

  /**
   * 获取数据长度
   * @param data 
   */
  _getLength(data: any): number {
    if (typeof data.length == 'number') {
      return data.length;
    } else if (data instanceof Object) {
      return Object.keys(data).length;
    } else {
      return undefined;
    }
  }

  /**
   * 格式转换
   * @param data 
   */
  _formatJson(data: any, key?: string) {
    let resp: Meta;
    if (typeof data == 'number') {
      resp = new Meta('number', (data + '').length, key ? key : 0, this._scientificCounting(data));
      return resp;
    } else if (typeof data == 'string') {
      resp = new Meta('string', (data + '').length, key ? key : 0, data);
      return resp;
    } else if (data === null) {
      resp = new Meta('null', 1, key ? key : 0, 'null');
      return resp;
    } else if (typeof data == 'boolean') {
      resp = new Meta('boolean', 1, key ? key : 0, data);
      return resp;
    } else if (typeof data == 'object' && (data instanceof Array || data instanceof Object)) {
      resp = new Meta(data instanceof Array ? 'array' : 'object', Object.keys(data).length, key ? key : (data instanceof Array ? 'array' : 'object'), []);
      for (let k in data) {
        (resp.value instanceof Array) && resp.value.push(this._formatJson(data[k], k));
      }
      return resp;
    }
  }

  /**
   * 生成模版
   * @param e 
   * @param metaLs 
   */
  _toggleTemp(e: Event, metaLs: Meta[] | string | number | boolean): void {
    let targetEl = $(e.target).parent().siblings('.flex_1'),
      ul;
    $(e.target).toggleClass('active');
    if (targetEl.children('.bz').length) {
      targetEl.children('.bz').remove();
      return;
    }
    targetEl.append('<ul style="margin-top: 10px;" class="bz"></ul>');
    ul = targetEl.children('.bz');
    metaLs instanceof Array && metaLs.forEach((meta, i: number) => {
      let htmlLi = '';
      htmlLi += `
        <li class="flex-box">
        <div style="width: 25px; height: 25px;">
        ${(meta.type == 'object' || meta.type == 'array') ? `<div class="arrow-container" eq="${i}"></div>` : ''}
        </div>
        <div>${meta.key}&nbsp;&nbsp;:&nbsp;&nbsp;</div>
        <div class="flex_1">
          ${(() => {
          let spanStr = '';
          if (meta.type == 'array' || meta.type == 'object')
            spanStr += "<span>" + meta.type + "</span>";
          switch (meta.type) {
            case 'number':
              spanStr += `<span style="color: ${this._color.number};">` + this._scientificCounting(meta.value) + '</span>';
              break;
            case 'string':
              spanStr += `<span style="${this._color.string};">"${(meta.value + '').replace(/\</g, '&lt;')}"</span>`;
              break;
            case 'null':
              spanStr += `<span style="color: ${this._color.null};">' + meta.value + '</span>`;
              break;
            case 'boolean':
              spanStr += `<span style="color: ${this._color.boolean};">` + meta.value + '</span>';
              break;
          }
          spanStr += "&nbsp;&nbsp";
          switch (meta.type) {
            case 'array':
              spanStr += '<span">[&nbsp;' + meta.length + '&nbsp;]</span>';
              break;
            case 'object':
              spanStr += '<span">{&nbsp;' + meta.length + '&nbsp;}</span>';
              break;
          }
          return spanStr;
        })()}
        </div>
      </li>`;
      ul.append(htmlLi);
      ul.children('li').eq(i).find('.arrow-container').on('click', (e) => {
        e = event;
        this._toggleTemp(e, meta.value);
      });
    });

  }

  /**
   * 科学计数法换算（误差极大）
   * @param number
   */
  _scientificCounting(number: any): string {
    // 10的21次幂开始 转为科学计数法，没办法，即使这么写了，精度还是有很大问题
    const reg = /^(\d+)\.?(\d*)\e([+|-])(\d+)$/,
      nTs = number.toString();
    if (!reg.test(nTs)) { return number; }
    let resp = reg.exec(nTs),
      len = Number(resp[4]) - resp[2].length, // 幂值
      numRes = resp[1] + resp[2];
    for (let i = 0; i < len; i++) {
      numRes += '0';
    }
    return numRes;
  }

  /**
   * 缩进json
   */
  _indentJson(data: JSON): string {
    let resp = JSON.stringify(this._jsonConfig.data, (key, value) => {
      return value;
    }, 2);
    return resp;
  }
}

// 配置参数
export class JsonModelConfig {
  data: JSON;
  name: string;
  length: number;
  type: string;
  mode: string = 'fold' || 'view';
}

class Meta {
  type: string;
  length: number;
  key: string | number;
  value: Meta[] | string | number | boolean;
  isactive: boolean = false;
  constructor(type: string, length: number, key: string | number, value?: Meta[] | string | number | boolean) {
    this.type = type;
    this.length = length;
    this.key = key;
    this.value = value;
  }
}